/******************************************************************************
 * Copyright (C) 2011 ShenZhen ComTop Information Technology Co.,Ltd
 * All Rights Reserved.
 * 本软件为深圳康拓普开发研制。未经本公司正式书面同意，其他任何个人、团体不得使用、
 * 复制、修改或发布本软件.
 *****************************************************************************/

package com.code.common.excel.officeexport.excel.tag;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;

import com.code.common.excel.officeexport.excel.exception.ExportExcelException;

/**
 * excel导出的处理主体类.
 * 
 * @author 李广
 * @since 1.0
 * @version 2005-1-26 李广 新建
 */
public class ExportExcelMan {
    
    /** 日志管理类对象 */
    private final Logger loger = Logger.getLogger(TageList.class);
    
    /** 导入到excel文件的数据 */
    private Map<String, Object> dataSource;
    
    /** excel模板文件 */
    private HSSFWorkbook workbook;
    
    /** 模板sheet的名称,以供删除excel中这些模板使用 */
    private final Set<String> delSheetName;
    
    /**
     * 默认构造函数
     */
    public ExportExcelMan() {
        this.delSheetName = new HashSet<String>();
    }
    
    /**
     * 设置数据源.
     * 
     * @param dataSource 数据源.
     */
    public void setDatatSource(Map<String, Object> dataSource) {
        this.dataSource = dataSource;
    }
    
    /**
     * 读取excel模板文件的信息.以便生成的excel文件使用.
     * 
     * @param templateFileName 模板文件的名称,包括路径.
     * @throws ExportExcelException excel导出异常.
     */
    public void readExcelFile(String templateFileName) throws ExportExcelException {
        try {
            FileInputStream objFileIn = new FileInputStream(templateFileName);
            POIFSFileSystem objPOIFile = new POIFSFileSystem(objFileIn);
            this.workbook = new HSSFWorkbook(objPOIFile);
            objFileIn.close();
        } catch (FileNotFoundException ex) {
            loger.error("指定的模板excel文件不存在:" + templateFileName, ex);
            throw new ExportExcelException("指定的模板excel文件不存在.", ex);
        } catch (IOException ex) {
            loger.error("Open file IO error.", ex);
            throw new ExportExcelException("Open file IO error.", ex);
        }
    }
    
    /**
     * 向excel文件中填充数据.
     */
    public void fillData() {
        fillData(null, null, 0);
    }
    
    /**
     * 向excel文件的指定sheet中填充数据.
     * 
     * @param sheetName excel中指定sheet的名称.
     */
    public void fillData(String sheetName) {
        fillData(sheetName, null, 0);
    }
    
    /**
     * 向excel文件中的指定sheet中填充数据,当指定的dataName中的数据记录数多于pageSize 指定的记录数时就分页.
     * 
     * @param sheetName excel中指定sheet的名称.
     * @param dataName 数据源的名称,分页就是根据这个数据源的记录数来判断的.
     * @param pageSize 分页时每页的记录数
     */
    @SuppressWarnings("unchecked")
    public void fillData(String sheetName, String dataName, int pageSize) {
        List<String> lstTag = new ArrayList<String>();
        // 如果以后标签有扩展了这里就要添加相应的标签名称.
        lstTag.add("object");
        lstTag.add("iterator");
        lstTag.add("sum");
        
        String strNewSheetName;
        int iId;
        HSSFSheet objSheet;
        // FIXME 重构点：dataSource, lstDataList, lstTempList 里面装的是什么类型不明确，需要重构
        List<Object> lstDataList;
        List<Object> lstTempList;
        TageObject objTagObject;
        TageList objTagList;
        TageSum objSum;
        
        if (sheetName == null) {
            objSheet = this.workbook.getSheetAt(0);
        } else {
            objSheet = this.workbook.getSheet(sheetName);
        }
        Map<String, List<ExcelCell>> objAllTag = ExcelUtil.getAllTag(objSheet, lstTag);
        
        if (pageSize < 1) {
            objTagObject = new TageObject();
            objTagObject.setValue(objSheet, this.dataSource, objAllTag.get("object"));
            objTagList = new TageList();
            objTagList.setValue(objSheet, this.dataSource, objAllTag.get("iterator"));
            objSum = new TageSum();
            objSum.setValue(objSheet, this.dataSource, objAllTag.get("sum"));
            return;
        }
        
        int iPageCount = getPageCount(dataName, pageSize);
        lstDataList = (ArrayList<Object>) this.dataSource.get(dataName);
        lstTempList = new ArrayList<Object>();
        HSSFSheet objNewSheet;
        
        for (int i = 0; i < iPageCount; i++) {
            strNewSheetName = sheetName + i;
            objNewSheet = clone(sheetName, strNewSheetName);
            lstTempList.clear();
            for (int j = 0; j < pageSize; j++) {
                iId = i * pageSize + j;
                if (iId < lstDataList.size()) {
                    lstTempList.add(lstDataList.get(iId));
                }
            }
            this.dataSource.put(dataName, lstTempList);
            
            objTagObject = new TageObject();
            objTagObject.setValue(objNewSheet, this.dataSource, objAllTag.get("object"));
            objTagList = new TageList();
            objTagList.setValue(objNewSheet, dataSource, objAllTag.get("iterator"));
            objSum = new TageSum();
            objSum.setValue(objSheet, this.dataSource, objAllTag.get("sum"));
            
        }
        removeSheet();
    }
    
    /**
     * 用于在servlet中调用,下载时使用.
     * 
     * @return 用于下载的HSSFWorkbook.
     */
    public HSSFWorkbook getWorkbook() {
        return this.workbook;
    }
    
    /**
     * 把生成的excel文件保存到磁盘上.
     * 
     * @param filename 保存的文件名.
     * @throws ExportExcelException 异常
     */
    public void saveFile(String filename) throws ExportExcelException {
        saveFile(filename, true);
    }
    
    /**
     * 把生成的excel文件保存到磁盘上,是否删除excel文件中的模板sheet.
     * 
     * @param filename 保存的文件名.
     * @param deleteTemplate 是否删除模板.
     * @throws ExportExcelException excel导出异常.
     */
    public void saveFile(String filename, boolean deleteTemplate) throws ExportExcelException {
        if (deleteTemplate) {
            removeSheet();
        }
        FileOutputStream objFileOut = null;
        try {
            objFileOut = new FileOutputStream(filename);
            this.workbook.write(objFileOut);
            objFileOut.close();
        } catch (FileNotFoundException ex) {
            loger.error("保存文件出错.", ex);
            throw new ExportExcelException("保存文件出错.", ex);
        } catch (IOException ex) {
            loger.error("Save file IO error.", ex);
            throw new ExportExcelException("Save file IO error.", ex);
        } finally {
            try {
                objFileOut.close();
            } catch (IOException e) {
                loger.error("Save file IO error.", e);
                throw new ExportExcelException("Save file IO error.", e);
            }
        }
    }
    
    /**
     * 用于在分页的时候根据模板sheet创建类似的sheet
     * 
     * @param templateSheetName 模板Sheet名称
     * @param newSheetName 新的Sheet名称
     * @return 新sheet对象
     */
    private HSSFSheet clone(String templateSheetName, String newSheetName) {
        int iTemplateSheetIndex = this.workbook.getSheetIndex(templateSheetName);
        String strNewSheetName = null;
        if (this.workbook.getSheetIndex(newSheetName) > -1) {
            strNewSheetName = newSheetName + "0";
        } else {
            strNewSheetName = newSheetName;
        }
        if (iTemplateSheetIndex > -1) {
            this.workbook.setSheetName(this.workbook.getNumberOfSheets() - 1, strNewSheetName);
            this.delSheetName.add(templateSheetName);
        }
        return this.workbook.getSheet(strNewSheetName);
    }
    
    /**
     *根据数据源中数据的多少和分页的每页的记录数算出分页的页数
     * 
     * @param dataName 数据源名称
     * @param pageSize 页大小
     * @return 页数量
     */
    
    @SuppressWarnings("unchecked")
    private int getPageCount(String dataName, int pageSize) {
        List<Object> lstData = (List<Object>) this.dataSource.get(dataName);
        return (int) Math.ceil((float) lstData.size() / pageSize);
    }
    
    /**
     * 删掉excel中的模板sheet
     * 
     * <pre>
     * 
     * </pre>
     */
    // 
    private void removeSheet() {
        Iterator<String> objIterator = this.delSheetName.iterator();
        int iTemplateIndex;
        String strTemplateName;
        while (objIterator.hasNext()) {
            strTemplateName = objIterator.next();
            iTemplateIndex = this.workbook.getSheetIndex(strTemplateName);
            this.workbook.removeSheetAt(iTemplateIndex);
        }
    }
    
}
